home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Sample Code / Standard File Samples / CustomGetFolder / CustomGetFolder.c < prev    next >
Encoding:
Text File  |  1995-06-16  |  11.9 KB  |  388 lines  |  [TEXT/MMCC]

  1. //
  2. //    Application:    CustomGetFolder
  3. //    
  4. //    Description:    This demonstrates a CustomGetDialog for selecting a folder or volume.
  5. //                    This sample is based on Steve Falkenburg's sample of a few yesrs back,
  6. //                  the sample code in InsideMac:Files, and the Human Interface Guidelines
  7. //                    It also has Baloon Help strings for the Select button
  8. //
  9. //    Programmer:        David Hayward
  10. //                    Developer Technical Support
  11. //                    Apple Computer, Inc.
  12. //
  13. //    Environment:    Metrowerks C version 6, Universal Interfaces 2.0
  14. //
  15. //    History:        4/20/95
  16. //                      first draft
  17. //                    5/12/95
  18. //                      updated project for Metrowerks
  19. //    
  20.  
  21.  
  22. #include <Dialogs.h>
  23. #include <Fonts.h>
  24. #include <Types.h>
  25. #include <Gestalt.h>
  26. #include <Resources.h>
  27. #include <Controls.h>
  28. #include <StandardFile.h>
  29. #include <TextUtils.h>
  30. #include <Files.h>
  31. #include <Folders.h>
  32. #include <Traps.h>
  33. #include <Script.h>
  34. #include <Aliases.h>
  35.  
  36. #include "InitMac.h"
  37.  
  38.  
  39. /**\
  40. |**| ==============================================================================
  41. |**| DEFINES
  42. |**| ==============================================================================
  43. \**/
  44. #define    ReplyDialogID            128
  45. #define    ReplyDialogQuitItem        1
  46. #define    ReplyDialogAgainItem    2
  47.  
  48. #define    CustomGetFolderDialogID    129
  49. #define    kSelectItem                13
  50.  
  51. #define    kSelectStrRsrc            130
  52. #define    kDeskStrRsrc            131
  53.  
  54. #define    kCanSelectDesktop        true
  55.  
  56.  
  57. /**\
  58. |**| ==============================================================================
  59. |**| GLOBALS
  60. |**| ==============================================================================
  61. \**/
  62. FSSpec        gDeskFolderSpec;
  63. Str255        gSelectString = "\pSelect";        // hardcode default value in case res load fails
  64. Str63        gDesktopFName = "\pDesktop";    // hardcode default value in case res load fails
  65.  
  66.  
  67. /**\
  68. |**| ==============================================================================
  69. |**| FUNCTION PROTOTYPES
  70. |**| ==============================================================================
  71. \**/
  72. void            AppendStrToStr            ( StringPtr dst, StringPtr src, unsigned char maxDstLen );
  73. void            GetStringPtr            ( short id, StringPtr dest ) ;
  74. OSErr            CustomGetFolder            ( StandardFileReply *reply ) ;
  75. pascal short    MyDlgHook                ( short item, DialogPtr theDlg, Ptr userData ) ;
  76. pascal Boolean    MyFilterAllFiles        ( CInfoPBPtr pb, Ptr myDataPtr ) ;
  77. void            SetSelectButtonName        ( FSSpec *spec, DialogPtr theDlg ) ;
  78. Boolean            SameFile                ( FSSpec *spec1, FSSpec *spec2 ) ;
  79. OSErr            GetDeskFolderSpec        ( FSSpec *spec ) ;
  80. OSErr            MakeCanonFSSpec            ( FSSpec *spec ) ;
  81. short            MyAlert                    ( StandardFileReply *reply ) ;
  82.  
  83.  
  84. /*------------------------------------------------------------------------------*\
  85.     AppendStrToStr()
  86.  *------------------------------------------------------------------------------*
  87.         utility function to append one string to another
  88.         this function should be improved to handle errors
  89. \*------------------------------------------------------------------------------*/
  90. void AppendStrToStr ( StringPtr dst, StringPtr src, unsigned char maxDstLen)
  91. {
  92.     short        offset = dst[0]+1;
  93.     short        size   = src[0];
  94.     
  95.     if ( dst[0] + src[0] > maxDstLen)            // make sure were not too big
  96.         size = maxDstLen - dst[0];                // you should return a warning here
  97.     BlockMove( src+1, dst+offset, size);
  98.     dst[0] += size;
  99. }
  100.  
  101.     
  102. /*------------------------------------------------------------------------------*\
  103.     GetStringPtr()
  104.  *------------------------------------------------------------------------------*
  105.         utility function to get a string from a 'STR ' resource
  106.         this function should be improved to handle errors
  107. \*------------------------------------------------------------------------------*/
  108. void GetStringPtr ( short id, StringPtr dest )
  109. {
  110.     StringHandle    strHndl ;
  111.     
  112.     strHndl = GetString( id ) ;
  113.     if ( ResError() == noErr && strHndl != nil )
  114.     {
  115.         HLock( (Handle)strHndl ) ;
  116.         BlockMove( *strHndl, dest, (*strHndl)[0]+1) ;
  117.         HUnlock( (Handle)strHndl ) ;
  118.         ReleaseResource( (Handle)strHndl ) ;
  119.     }
  120. }
  121.  
  122.     
  123. /*  CustomGetFolder */
  124. OSErr CustomGetFolder ( StandardFileReply *reply )
  125. {
  126.     Point        where = {-1,-1};                /* center dialog on main screen */
  127.     OSErr        err;
  128.     Boolean        targetIsFolder,wasAliased;
  129.     
  130.     /* initialize global data */
  131.     GetDeskFolderSpec( &gDeskFolderSpec );
  132.     GetStringPtr( kSelectStrRsrc, gSelectString ) ;
  133.     GetStringPtr( kDeskStrRsrc, gDesktopFName ) ;
  134.         
  135.     CustomGetFile(    MyFilterAllFiles,            /* file filter proc */
  136.                     -1,                            /* number of file types */
  137.                     nil,                        /* array of file types */
  138.                     reply,                        /* the StandardFileReply structure */
  139.                     CustomGetFolderDialogID,    /* the dialog's ID */
  140.                     where,                        /* position of dialog on screen */
  141.                     (DlgHookYDProcPtr)MyDlgHook,/* the Dialog Hook procedure */
  142.                     nil,                        /* no modal dialog filterProc */
  143.                     nil,                        /* no activeListPtr */
  144.                     nil,                        /* no activateProc */
  145.                     reply                        /* yourDataPtr: pass reply in so that */
  146.                 );                                /* MyDlgHook() can access current info */
  147.  
  148.     if ( !reply->sfGood )                        /* if the user hit cancel button */
  149.         return noErr ;                            /* then nothing more to do so return */
  150.         
  151.     
  152.     // if its an alias
  153.     {
  154.         err = ResolveAliasFile( &(reply->sfFile),
  155.                                 true,
  156.                                 &targetIsFolder,
  157.                                 &wasAliased);
  158.         if (err) return err ;
  159.     }
  160.     
  161.     
  162.     // if is not a directory or volume
  163.     if ( !reply->sfIsFolder && !reply->sfIsVolume )
  164.     {
  165.         FSSpec        tempSpec;
  166.         CInfoPBRec    infoPB;
  167.         Boolean        IsDirectory;
  168.  
  169.         tempSpec = reply->sfFile;
  170.         if (tempSpec.name[0] != '\0') return; //err
  171.  
  172.         infoPB.dirInfo.ioNamePtr = tempSpec.name;
  173.         infoPB.dirInfo.ioVRefNum = tempSpec.vRefNum;
  174.         infoPB.dirInfo.ioDrDirID = tempSpec.parID;
  175.         infoPB.dirInfo.ioFDirIndex = -1;
  176.         
  177.         err = PBGetCatInfo( &infoPB, false );
  178.         if (err) return err ;
  179.         
  180.         tempSpec.parID = infoPB.dirInfo.ioDrParID ;
  181.         
  182.         // make sure that it's a directory
  183.         IsDirectory = (infoPB.dirInfo.ioFlAttrib & 0x10) ;
  184.         if ( !IsDirectory ) return; //err
  185.         
  186.         reply->sfFile = tempSpec ;
  187.         reply->sfScript = infoPB.dirInfo.ioDrFndrInfo.frScript ;
  188.         reply->sfFlags = infoPB.dirInfo.ioDrUsrWds.frFlags ;
  189.         reply->sfIsFolder = (tempSpec.parID == 1) ? (0x00) : (0xFF) ;
  190.         reply->sfIsVolume = (tempSpec.parID == 1) ? (0xFF) : (0x00) ; ;
  191.     }
  192.  
  193. }
  194.  
  195.  
  196. pascal short MyDlgHook ( short item, DialogPtr theDlg, Ptr userData )
  197. {
  198.     StandardFileReply    *reply;
  199.     FSSpec                curSpec;
  200.     OSType                refCon;
  201.     static FSSpec        lastSpec;                /* remember and lastSpec so that we */
  202.                                                 /* don't update Select button needlessly    */
  203.     
  204.     refCon = GetWRefCon(theDlg);
  205.     if (refCon!=sfMainDialogRefCon)
  206.         return item;
  207.     
  208.     reply = (StandardFileReply*)userData;
  209.     
  210.     if (item == sfHookFirstCall)                 /* if the dialog is just about to appear */
  211.         lastSpec.vRefNum = -9999;                /* init to ridiculous value */
  212.     
  213.     if (item == sfHookNullEvent)                /* if we got a NullEvent */
  214.     {
  215.         if ( !SameFile( &(reply->sfFile), &lastSpec) )
  216.         {
  217.             curSpec = reply->sfFile ;
  218.             MakeCanonFSSpec( &curSpec );
  219.             SetSelectButtonName( &curSpec, theDlg);
  220.             lastSpec = reply->sfFile ;
  221.         }
  222.     }
  223.     
  224.     if ( item==kSelectItem)
  225.         item = sfItemOpenButton;
  226.         
  227.     return item;
  228. }
  229.  
  230.  
  231. /*------------------------------------------------------------------------------*\
  232.     MyFilterAllFiles()
  233.  *------------------------------------------------------------------------------*
  234.         A file filter proc that removes everything but directories from the 
  235.         CustomGetFile list.  This means that only Folders, Volumes, or Aliases of
  236.         Folders or Volumes will appear in the list.
  237. \*------------------------------------------------------------------------------*/
  238. pascal Boolean MyFilterAllFiles ( CInfoPBPtr pb, Ptr myDataPtr )
  239. {
  240.     if (pb->hFileInfo.ioFlAttrib & (1<<4))    /* file is a directory */
  241.         return false;
  242.     return true;
  243. }
  244.  
  245.  
  246. /*------------------------------------------------------------------------------*\
  247.     SetSelectButtonName()
  248.  *------------------------------------------------------------------------------*
  249.         This routine gets the "Select" button from the CustomGetFile dialog and
  250.         the filename string from the FSSpec parameter.  It set the buttons name 
  251.         to Select “filename”, truncating the filemane if needed.  The button is
  252.         dimmed if kCanSelectDesktop==false and the FSSpec=the desktop folder.
  253. \*------------------------------------------------------------------------------*/
  254. void SetSelectButtonName ( FSSpec *spec, DialogPtr theDlg )
  255. {
  256.     short            iType;
  257.     Handle            iHndl;
  258.     Rect            iRect;
  259.     Str63            selNameTrunc = "\p";
  260.     Str255            btnName = "\p";
  261.     short            btnWidth;
  262.     Boolean            hilited = true;
  263.     
  264.     if ( SameFile( spec, &gDeskFolderSpec) )
  265.     {
  266.         AppendStrToStr( selNameTrunc, gDesktopFName, 63 ) ;
  267.         hilited = kCanSelectDesktop;
  268.     }
  269.     else
  270.         AppendStrToStr( selNameTrunc, spec->name, 63 ) ;
  271.     
  272.     
  273.     GetDItem(theDlg,kSelectItem,&iType,&iHndl,&iRect);
  274.     
  275.     /* truncate select name to fit in button */
  276.     btnWidth = iRect.right - iRect.left;
  277.     btnWidth -= StringWidth(gSelectString);
  278.     btnWidth -= StringWidth("\p “”  ");
  279.     TruncString(btnWidth, selNameTrunc, smTruncMiddle);
  280.     
  281.     /* build button name string */
  282.     AppendStrToStr( btnName, gSelectString, 255 ) ;
  283.     AppendStrToStr( btnName, "\p “", 255) ;
  284.     AppendStrToStr( btnName, selNameTrunc, 255) ;
  285.     AppendStrToStr( btnName, "\p”", 255) ;
  286.     
  287.     SetCTitle((ControlRef)iHndl, btnName);
  288.  
  289.     if (hilited)
  290.         HiliteControl((ControlRef)iHndl,0);
  291.     else
  292.         HiliteControl((ControlRef)iHndl,255);        
  293. }
  294.  
  295.  
  296. /*------------------------------------------------------------------------------*\
  297.     SameFile()
  298.  *------------------------------------------------------------------------------*
  299.         Compares to FSSpec records to seeif there're the same. 
  300. \*------------------------------------------------------------------------------*/
  301. Boolean SameFile ( FSSpec *spec1, FSSpec *spec2 )
  302. {
  303.     if (spec1->vRefNum != spec2->vRefNum)
  304.         return false;
  305.     if (spec1->parID != spec2->parID)
  306.         return false;
  307.     if ( !EqualString( spec1->name, spec2->name, false, true ) )
  308.         return false;
  309.     return true;
  310. }
  311.  
  312.  
  313. /*------------------------------------------------------------------------------*\
  314.     GetDeskFolderSpec()
  315.  *------------------------------------------------------------------------------*
  316.         Returns a FSSpec record for the Desktop folder on the boot volume. 
  317. \*------------------------------------------------------------------------------*/
  318. OSErr GetDeskFolderSpec ( FSSpec *spec )
  319. {
  320.     OSErr err;
  321.     
  322.     spec->name[0] = '\0';
  323.     err = FindFolder(    kOnSystemDisk,
  324.                         kDesktopFolderType,
  325.                         kDontCreateFolder,
  326.                         &spec->vRefNum,
  327.                         &spec->parID);
  328.     if (err) return err;
  329.     return MakeCanonFSSpec( spec );
  330. }
  331.  
  332.  
  333. /*------------------------------------------------------------------------------*\
  334.     MakeCanonFSSpec()
  335.  *------------------------------------------------------------------------------*
  336.         Given a FSSpec record, convert it to the connonical format with 
  337.         file's vRefNum, parrent dirID, and name. 
  338. \*------------------------------------------------------------------------------*/
  339. OSErr MakeCanonFSSpec ( FSSpec *spec )
  340. {
  341.     OSErr        err ;
  342.     
  343.     err = FSMakeFSSpec( spec->vRefNum,
  344.                         spec->parID,
  345.                         spec->name,
  346.                         spec ) ;
  347.     return noErr ;
  348. }
  349.  
  350.  
  351. /*------------------------------------------------------------------------------*\
  352.     MyAlert()
  353.  *------------------------------------------------------------------------------*
  354.         output vital stats of the StandardFileReply to an Alert box
  355. \*------------------------------------------------------------------------------*/
  356. short MyAlert ( StandardFileReply *reply )
  357. {
  358.     Str255    goodStr, replStr ;
  359.  
  360.     NumToString( (long)reply->sfGood, goodStr ) ; 
  361.     NumToString( (long)reply->sfReplacing, replStr ) ; 
  362.     ParamText( goodStr, replStr, reply->sfFile.name, nil ) ;
  363.     return NoteAlert( ReplyDialogID, nil );
  364. }
  365.  
  366.  
  367. /*------------------------------------------------------------------------------*\
  368.     main()
  369.  *------------------------------------------------------------------------------*
  370.         initialize mamagers and the keep doing 
  371.         CustomGetFolder until the user has had enough
  372. \*------------------------------------------------------------------------------*/
  373. void main ( void )
  374. {
  375.     StandardFileReply    reply;
  376.     Point                where = {-1,-1};            /* center dialog on main screen */
  377.     
  378.     InitToolBox(2) ;
  379.  
  380.     do
  381.     {
  382.         CustomGetFolder( &reply );
  383.     }                                                            
  384.     while (MyAlert(&reply)==ReplyDialogAgainItem);    /* repeat until user has had enough */
  385. }
  386.  
  387.  
  388.